from flask import Flask, render_template, send_from_directory, Response, request, redirect, url_for, flash
import os
import mimetypes
from werkzeug.utils import secure_filename
import cv2

app = Flask(__name__)

# Configuration
VIDEO_FOLDER = 'static/videos'
ALLOWED_EXTENSIONS = {'mp4'}
MAX_CONTENT_LENGTH = 50 * 1024 * 1024  # 50MB max file size

# Set a secret key for session management (required for flash messages)
app.secret_key = 'your-secret-key-here'  # In production, use a random secret key

# Add video MIME type if not already registered
mimetypes.add_type('video/mp4', '.mp4')

def allowed_file(filename):
    """
    Check if the uploaded file has an allowed extension.
    """
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

@app.route('/')
def index():
    # Get list of all video files in the videos directory
    video_files = []
    if os.path.exists(VIDEO_FOLDER):
        for filename in os.listdir(VIDEO_FOLDER):
            if filename.lower().endswith('.mp4'):
                video_files.append(filename)
    
    return render_template('index.html', videos=video_files, get_video_duration=get_video_duration, get_video_thumbnail=get_video_thumbnail)

@app.route('/videos/<path:filename>')
def serve_video(filename):
    # Check if file exists
    filepath = os.path.join(VIDEO_FOLDER, filename)
    if not os.path.exists(filepath):
        return "Video not found", 404
    
    # Get the MIME type for the video file
    mime_type, _ = mimetypes.guess_type(filepath)
    
    # Handle range requests for seeking functionality
    range_header = request.headers.get('Range', None)
    if range_header:
        # 使用改进的范围请求处理函数
        response = get_video_range_response(filepath, range_header)
        if response:
            return response
    
    # 如果没有范围请求，发送整个文件（用于初始加载）
    def generate():
        with open(filepath, 'rb') as f:
            while True:
                chunk = f.read(4096)  # Read in chunks of 4KB
                if not chunk:
                    break
                yield chunk
    
    # Get the total size of the video file
    file_size = os.path.getsize(filepath)
    
    return Response(
        generate(),
        mimetype=mime_type,
        headers={
            "Accept-Ranges": "bytes",
            "Content-Range": f"bytes */{file_size}",
            "Content-Length": str(file_size)
        }
    )

@app.route('/play/<path:filename>')
def play_video(filename):
    """
    Render the video player page for a specific video.
    """
    # Check if file exists
    filepath = os.path.join(VIDEO_FOLDER, filename)
    if not os.path.exists(filepath):
        return "Video not found", 404
    
    return render_template('video_player.html', video_name=filename, get_video_duration=get_video_duration)

@app.route('/upload', methods=['GET', 'POST'])
def upload_video():
    """
    Handle video file uploads.
    """
    if request.method == 'POST':
        # Check if the post request has the file part
        if 'video_file' not in request.files:
            flash('No file selected', 'error')
            return redirect(request.url)
        
        file = request.files['video_file']
        
        # If user does not select a file, browser submits an empty file without filename
        if file.filename == '':
            flash('No file selected', 'error')
            return redirect(request.url)
        
        # Check if the file is allowed and has valid extension
        if file and allowed_file(file.filename):
            # Secure the filename to prevent directory traversal attacks
            filename = secure_filename(file.filename)
            
            # Create the videos directory if it doesn't exist
            os.makedirs(VIDEO_FOLDER, exist_ok=True)
            
            # Save the file to the videos directory
            filepath = os.path.join(VIDEO_FOLDER, filename)
            try:
                file.save(filepath)
                flash(f'Video "{filename}" uploaded successfully!', 'success')
            except Exception as e:
                flash(f'Error saving video: {str(e)}', 'error')
        else:
            flash('Invalid file type. Only .mp4 files are allowed.', 'error')
    
    return redirect(url_for('index'))

# Function to extract first frame from video file
def get_video_thumbnail(filename):
    """
    Extract the first frame of a video file as a thumbnail.
    Returns base64 encoded image data or None if failed.
    """
    try:
        # Get full path to video file
        filepath = os.path.join(VIDEO_FOLDER, filename)
        
        # Open video file using OpenCV
        cap = cv2.VideoCapture(filepath)
        
        # Check if video opened successfully
        if not cap.isOpened():
            return None
            
        # Read the first frame
        ret, frame = cap.read()
        
        # Release the video capture object
        cap.release()
        
        if not ret:
            return None
            
        # Encode the frame as JPEG (you can also use PNG)
        success, buffer = cv2.imencode('.jpg', frame)
        
        if not success:
            return None
            
        # Convert to base64 string for embedding in HTML
        import base64
        thumbnail_data = base64.b64encode(buffer).decode('utf-8')
        return f"data:image/jpeg;base64,{thumbnail_data}"
    except Exception as e:
        print(f"Error extracting thumbnail: {e}")
        return None

# Function to estimate video duration (simplified approach)
def get_video_duration(filename):
    """
    Estimate the duration of a video file.
    This is a simplified implementation that returns a placeholder time.
    In a real application, you might use libraries like moviepy or ffmpeg-python
    to accurately extract video metadata including duration.
    """
    # For demonstration purposes, we'll return a fixed duration
    # In practice, you would parse the actual video file metadata
    return "2:30"  # Placeholder for 2 minutes and 30 seconds

# 添加一个更完善的视频服务函数，确保范围请求正确处理
def get_video_range_response(filepath, range_header):
    """
    处理视频文件的范围请求（Range Requests）以支持进度条拖动功能。
    """
    file_size = os.path.getsize(filepath)
    
    # 解析 Range 请求头
    if not range_header or not range_header.startswith("bytes="):
        return None
    
    ranges = range_header.split("=")[1].split("-")
    range_start = int(ranges[0])
    range_end = int(ranges[1]) if len(ranges) > 1 and ranges[1] else file_size - 1
    
    # 确保范围有效
    range_end = min(range_end, file_size - 1)
    
    def generate():
        with open(filepath, 'rb') as f:
            f.seek(range_start)
            length = range_end - range_start + 1
            
            while length > 0:
                chunk_size = min(4096, length)
                chunk = f.read(chunk_size)
                if not chunk:
                    break
                yield chunk
                length -= len(chunk)
    
    return Response(
        generate(),
        status=206,
        mimetype='video/mp4',
        headers={
            "Accept-Ranges": "bytes",
            "Content-Range": f"bytes {range_start}-{range_end}/{file_size}",
            "Content-Length": str(range_end - range_start + 1)
        }
    )

if __name__ == '__main__':
    app.run(debug=True, host="0.0.0.0")